<!DOCTYPE html>
<html lang="en">
<head>
  <title>Plugin Development - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/advanced/plugin.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/advanced/plugin.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Plugin Development</h1>
    <p>Plugins is the most important feature in Egg framework. It keeps Egg simple, stable and efficient, and also it can make the best reuse of business logic, to build an entire ecosystem. Someone should be confused:</p>
<ul>
<li>Since Koa already has the mechanism of middleware, what's the point of the Egg's plugins</li>
<li>What is the differences between middleware, plugin and application, what is the relationship</li>
<li>How can I use the plugin</li>
<li>How do I build a plugin</li>
<li>...</li>
</ul>
<p>As we've already explained some these points in Chapter <a href="../basics/plugin.html">using plugins</a> before. Now we are going through how to build a plugin.</p>
<h2 id="plugin-development"><a class="markdown-anchor" href="#plugin-development">#</a> Plugin Development</h2>
<h3 id="quick-start-with-scaffold"><a class="markdown-anchor" href="#quick-start-with-scaffold">#</a> Quick Start with Scaffold</h3>
<p>You can choose <a href="https://github.com/eggjs/egg-boilerplate-plugin" target="_blank" rel="noopener">plugin</a> scaffold in <a href="https://github.com/eggjs/egg-init" target="_blank" rel="noopener">egg-init</a> for quick start.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ egg-init --<span class="built_in">type</span>=plugin egg-hello</span><br><span class="line">$ <span class="built_in">cd</span> egg-hello</span><br><span class="line">$ npm i</span><br><span class="line">$ npm <span class="built_in">test</span></span><br></pre></td></tr></table></figure>
<h2 id="directory-of-plugin"><a class="markdown-anchor" href="#directory-of-plugin">#</a> Directory of Plugin</h2>
<p>Plugin is actually a 'mini application', directory of plugin is as below</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">. egg-hello</span><br><span class="line">├── package.json</span><br><span class="line">├── app.js (optional)</span><br><span class="line">├── agent.js (optional)</span><br><span class="line">├── app</span><br><span class="line">│   ├── extend (optional)</span><br><span class="line">│   |   ├── helper.js (optional)</span><br><span class="line">│   |   ├── request.js (optional)</span><br><span class="line">│   |   ├── response.js (optional)</span><br><span class="line">│   |   ├── context.js (optional)</span><br><span class="line">│   |   ├── application.js (optional)</span><br><span class="line">│   |   └── agent.js (optional)</span><br><span class="line">│   ├── service (optional)</span><br><span class="line">│   └── middleware (optional)</span><br><span class="line">│       └── mw.js</span><br><span class="line">├── config</span><br><span class="line">|   ├── config.default.js</span><br><span class="line">│   ├── config.prod.js</span><br><span class="line">|   ├── config.test.js (optional)</span><br><span class="line">|   ├── config.local.js (optional)</span><br><span class="line">|   └── config.unittest.js (optional)</span><br><span class="line">└── test</span><br><span class="line">    └── middleware</span><br><span class="line">        └── mw.test.js</span><br></pre></td></tr></table></figure>
<p>It is almost the same as the application directory, what's the difference?</p>
<ol>
<li>
<p>Plugin have no independant router or controller. This is because:</p>
<ul>
<li>Usually routers are strongly bound to application, it is not fit here.</li>
<li>An application might have plenty of dependant plugins, routers of plugin are very possible conflict with others. It would be a disaster.</li>
<li>If you really need a general router, you should implement it as middleware of the plugin.</li>
</ul>
</li>
<li>
<p>The specific information of plugin should be declared in the  <code>package.json</code> of <code>eggPlugin</code>：</p>
<ul>
<li>
<p><code>{String} name</code> - plugin name(required), it must be unique, it will be used in the config of the dependencies of plugin.</p>
</li>
<li>
<p><code>{Array} dependencies</code> - strong dependant plugins list of current plugin(if one of these plugins here is not found, application's startup will be failed)</p>
</li>
<li>
<p><code>{Array} optionalDependencies</code> - optional dependencies list of this plugin.(if these plugins are not activated, only warnings would be occurred, and will not affect the startup of the application).</p>
</li>
<li>
<p><code>{Array} env</code> - this option is available only when specify the environment. The list of env please refer to <a href="../basics/env.html">env</a>. This is optional, most time you can leave it.</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-rpc"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"rpc"</span>,</span><br><span class="line">    <span class="attr">"dependencies"</span>: [ <span class="string">"registry"</span> ],</span><br><span class="line">    <span class="attr">"optionalDependencies"</span>: [ <span class="string">"vip"</span> ],</span><br><span class="line">    <span class="attr">"env"</span>: [ <span class="string">"local"</span>, <span class="string">"test"</span>, <span class="string">"unittest"</span>, <span class="string">"prod"</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li>
<p>No <code>plugin.js</code>：</p>
<ul>
<li><code>eggPlugin.dependencies</code> is for declaring dependencies only, not for importing, nor activating.</li>
<li>If you want to manage multiple plugins, you should do it in<a href="./framework.html">upper framework</a></li>
</ul>
</li>
</ol>
<h2 id="dependencies-management-of-plugins"><a class="markdown-anchor" href="#dependencies-management-of-plugins">#</a> Dependencies Management of Plugins</h2>
<p>The dependencies are managed by plugin himself, this is different from middleware. Before loading plugins, application will read <code>eggPlugin &gt; dependencies</code> and <code>eggPlugin &gt; optionalDependencies</code> from <code>package.json</code>, and then sort out the loading orders according to their relationships, for example, the loading order of the following plugins is <code>c =&gt; b =&gt; a</code></p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">// plugin a</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-plugin-a"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"a"</span>,</span><br><span class="line">    <span class="attr">"dependencies"</span>: [ <span class="string">"b"</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// plugin b</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-plugin-b"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"b"</span>,</span><br><span class="line">    <span class="attr">"optionalDependencies"</span>: [ <span class="string">"c"</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// plugin c</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-plugin-c"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"c"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>** Attention: The values in <code>dependencies</code> and <code>optionalDependencies</code> are the <code>eggPlugin.name</code> of plugins, not <code>package.name</code>. **</p>
<p>The <code>dependencies</code> and <code>optionalDependencies</code> is studied from <code>npm</code>, most time we are using <code>dependencies</code>, it is recommended. There are about two situations to apply the <code>optionalDependencies</code>:</p>
<ul>
<li>Only be dependant in specific environment: for example, a authentication plugin, only depends on the mock plugin in development environment.</li>
<li>Weakly depending, for example: A depends on B, but without B, A can take other choice</li>
</ul>
<p>Pay attention: if you are using <code>optionalDependencies</code>, framework won't verify the activation of these dependencies, they are only for sorting loading orders. In such situation, the plugin will go through other ways such as <code>interface detection</code> to decide processing logic.</p>
<h2 id="what-can-plugin-do"><a class="markdown-anchor" href="#what-can-plugin-do">#</a> What can plugin do</h2>
<p>We've discussed what plugin is. Now what can it do?</p>
<h3 id="built-in-objects-api-extension"><a class="markdown-anchor" href="#built-in-objects-api-extension">#</a> Built-in Objects API Extension</h3>
<p>Extend the built-in objects of the framework, just like the application</p>
<ul>
<li><code>app/extend/request.js</code> - extends Koa#Request object</li>
<li><code>app/extend/response.js</code> - extends Koa#Response object</li>
<li><code>app/extend/context.js</code> - extends Koa#Context object</li>
<li><code>app/extend/helper.js</code> - extends Helper object</li>
<li><code>app/extend/application.js</code> - extends Application object</li>
<li><code>app/extend/agent.js</code> - extends Agent object</li>
</ul>
<h3 id="insert-custom-middlewares"><a class="markdown-anchor" href="#insert-custom-middlewares">#</a> Insert Custom Middlewares</h3>
<ol>
<li>
<p>First, define and implement middleware under directory <code>app/middleware</code></p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> staticCache = <span class="built_in">require</span>(<span class="string">'koa-static-cache'</span>);</span><br><span class="line"><span class="keyword">const</span> assert = <span class="built_in">require</span>(<span class="string">'assert'</span>);</span><br><span class="line"><span class="keyword">const</span> mkdirp = <span class="built_in">require</span>(<span class="string">'mkdirp'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function">(<span class="params">options, app</span>) =&gt;</span> &#123;</span><br><span class="line">  assert.strictEqual(<span class="keyword">typeof</span> options.dir, <span class="string">'string'</span>, <span class="string">'Must set `app.config.static.dir` when static plugin enable'</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ensure directory exists</span></span><br><span class="line">  mkdirp.sync(options.dir);</span><br><span class="line"></span><br><span class="line">  app.loggers.coreLogger.info(<span class="string">'[egg-static] starting static serve %s -&gt; %s'</span>, options.prefix, options.dir);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> staticCache(options);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>Insert middleware to the appropriate position in <code>app.js</code>(e.g. insert static middleware before bodyParser )</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> assert = <span class="built_in">require</span>(<span class="string">'assert'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// insert static middleware before bodyParser</span></span><br><span class="line">  <span class="keyword">const</span> index = app.config.coreMiddleware.indexOf(<span class="string">'bodyParser'</span>);</span><br><span class="line">  assert(index &gt;= <span class="number">0</span>, <span class="string">'bodyParser highly needed'</span>);</span><br><span class="line"></span><br><span class="line">  app.config.coreMiddleware.splice(index, <span class="number">0</span>, <span class="string">'static'</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="initialization-on-application-starting"><a class="markdown-anchor" href="#initialization-on-application-starting">#</a> Initialization on Application Starting</h3>
<ul>
<li>
<p>If you want to read some local config before startup</p>
  <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// $&#123;plugin_root&#125;/app.js</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.customData = fs.readFileSync(path.join(app.config.baseDir, <span class="string">'data.bin'</span>));</span><br><span class="line"></span><br><span class="line">  app.coreLogger.info(<span class="string">'read data ok'</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>If you want to do some async starting business, you can do it with <code>app.beforeStart</code> API</p>
  <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// $&#123;plugin_root&#125;/app.js</span></span><br><span class="line"><span class="keyword">const</span> MyClient = <span class="built_in">require</span>(<span class="string">'my-client'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.myClient = <span class="keyword">new</span> MyClient();</span><br><span class="line">  app.myClient.on(<span class="string">'error'</span>, err =&gt; &#123;</span><br><span class="line">    app.coreLogger.error(err);</span><br><span class="line">  &#125;);</span><br><span class="line">  app.beforeStart(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> app.myClient.ready();</span><br><span class="line">    app.coreLogger.info(<span class="string">'my client is ready'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>You can add starting business of agent with <code>agent.beforeStart</code> API</p>
  <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// $&#123;plugin_root&#125;/agent.js</span></span><br><span class="line"><span class="keyword">const</span> MyClient = <span class="built_in">require</span>(<span class="string">'my-client'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">agent</span> =&gt;</span> &#123;</span><br><span class="line">  agent.myClient = <span class="keyword">new</span> MyClient();</span><br><span class="line">  agent.myClient.on(<span class="string">'error'</span>, err =&gt; &#123;</span><br><span class="line">    agent.coreLogger.error(err);</span><br><span class="line">  &#125;);</span><br><span class="line">  agent.beforeStart(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">await</span> agent.myClient.ready();</span><br><span class="line">    agent.coreLogger.info(<span class="string">'my client is ready'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="setup-schedule-task"><a class="markdown-anchor" href="#setup-schedule-task">#</a> Setup Schedule Task</h3>
<ol>
<li>
<p>Setup dependencies of schedule plugin in <code>package.json</code></p>
 <figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"your-plugin"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"your-plugin"</span>,</span><br><span class="line">    <span class="attr">"dependencies"</span>: [ <span class="string">"schedule"</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>Create a new file in <code>${plugin_root}/app/schedule/</code> directory to edit your schedule task</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line">exports.schedule = &#123;</span><br><span class="line">  type: <span class="string">'worker'</span>,</span><br><span class="line">  cron: <span class="string">'0 0 3 * * *'</span>,</span><br><span class="line">  <span class="comment">// interval: '1h',</span></span><br><span class="line">  <span class="comment">// immediate: true,</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">exports.task = <span class="keyword">async</span> ctx =&gt; &#123;</span><br><span class="line">  <span class="comment">// your logic code</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="best-practice-of-global-instance-plugin"><a class="markdown-anchor" href="#best-practice-of-global-instance-plugin">#</a> Best Practice of Global Instance Plugin</h3>
<p>Some plugins are made to introduce existing service into framework, like <a href="https://github.com/eggjs/egg-mysql" target="_blank" rel="noopener">egg-mysql</a>,<a href="https://github.com/eggjs/egg-oss" target="_blank" rel="noopener">egg-oss</a>.They all need to create corresponding instance in application. We notice that there are some common problems when developing this kind of plugins:</p>
<ul>
<li>Use different instances of the same service in one application(e.g:connect to two different MySQL Databases)</li>
<li>Dynamically initialize connection after getting config from other service(gets MySQL server address from configuration center and then creates connection)</li>
</ul>
<p>If each plugin makes their own implementation, all sorts of configs  and  initializations will be chaotic. So the framework supplies the  <code>app.addSingleton(name, creator)</code> API to unify the creation of this kind of services. Note that while using the <code>app.addSingleton(name, creator)</code> method, the configuration file must have the <code>client</code> or <code>clients</code> key configuration as the <code>config</code> to the <code>creator</code> function.</p>
<h4 id="writing-plugin"><a class="markdown-anchor" href="#writing-plugin">#</a> Writing Plugin</h4>
<p>We simplify the <a href="https://github.com/eggjs/egg-mysql" target="_blank" rel="noopener">egg-mysql</a> plugin and to see how to write such a plugin:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// egg-mysql/app.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// The first parameter mysql defines the field  mounted to app, we can access MySQL singleton instance via `app.mysql`</span></span><br><span class="line">  <span class="comment">// The second parameter createMysql accepts two parameters (config, app), and then returns a MySQL instance</span></span><br><span class="line">  app.addSingleton(<span class="string">'mysql'</span>, createMysql);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param  &#123;Object&#125; config   The config which already processed by the framework. If the application configured multiple MySQL instances, each config would be passed in and call multiple createMysql</span></span><br><span class="line"><span class="comment"> * @param  &#123;Application&#125; app  the current application</span></span><br><span class="line"><span class="comment"> * @return &#123;Object&#125;          return the created MySQL instance</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createMysql</span>(<span class="params">config, app</span>) </span>&#123;</span><br><span class="line">  assert(config.host &amp;&amp; config.port &amp;&amp; config.user &amp;&amp; config.database);</span><br><span class="line">  <span class="comment">// create instance</span></span><br><span class="line">  <span class="keyword">const</span> client = <span class="keyword">new</span> Mysql(config);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// check before start the application</span></span><br><span class="line">  app.beforeStart(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> rows = <span class="keyword">await</span> client.query(<span class="string">'select now() as currentTime;'</span>);</span><br><span class="line">    app.coreLogger.info(<span class="string">`[egg-mysql] init instance success, rds currentTime: <span class="subst">$&#123;rows[<span class="number">0</span>].currentTime&#125;</span>`</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> client;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>The initialization function also support <code>Async function</code>, convenient for some special plugins that need to be asynchronous to get some configuration files.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">createMysql</span>(<span class="params">config, app</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// get mysql configurations asynchronous</span></span><br><span class="line">  <span class="keyword">const</span> mysqlConfig = <span class="keyword">await</span> app.configManager.getMysqlConfig(config.mysql);</span><br><span class="line">  assert(mysqlConfig.host &amp;&amp; mysqlConfig.port &amp;&amp; mysqlConfig.user &amp;&amp; mysqlConfig.database);</span><br><span class="line">  <span class="comment">// create instance</span></span><br><span class="line">  <span class="keyword">const</span> client = <span class="keyword">new</span> Mysql(mysqlConfig);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// check before start the application</span></span><br><span class="line">  <span class="keyword">const</span> rows = <span class="keyword">await</span> client.query(<span class="string">'select now() as currentTime;'</span>);</span><br><span class="line">  app.coreLogger.info(<span class="string">`[egg-mysql] init instance success, rds currentTime: <span class="subst">$&#123;rows[<span class="number">0</span>].currentTime&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> client;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>As you can see, all we need to do for this plugin is passing in the field that need to be mounted and the corresponding initialization function. Framework will be in charge of managing all the configs and the ways to access the instances.</p>
<h4 id="application-layer-usage-case"><a class="markdown-anchor" href="#application-layer-usage-case">#</a> Application Layer Usage Case</h4>
<h5 id="single-instance"><a class="markdown-anchor" href="#single-instance">#</a> Single Instance</h5>
<ol>
<li>
<p>Declare MySQL config in config file</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  mysql: &#123;</span><br><span class="line">    client: &#123;</span><br><span class="line">      host: <span class="string">'mysql.com'</span>,</span><br><span class="line">      port: <span class="string">'3306'</span>,</span><br><span class="line">      user: <span class="string">'test_user'</span>,</span><br><span class="line">      password: <span class="string">'test_password'</span>,</span><br><span class="line">      database: <span class="string">'test'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>Access database through <code>app.mysql</code> directly</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/post.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> posts = <span class="keyword">await</span> <span class="keyword">this</span>.app.mysql.query(sql, values);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h5 id="multiple-instances"><a class="markdown-anchor" href="#multiple-instances">#</a> Multiple Instances</h5>
<ol>
<li>
<p>Of course we need to configure MySQL in the config file, but different from single instance, we need to add  <code>clients</code> in the config to declare the configuration of different instances. meanwhile, the <code>default</code> field can be used to configure the shared configuration in multiple instances(e.g. host and port). Note that in this case,should use <code>get</code> function to specify the corresponding instance(eg: use <code>app.mysql.get('db1').query()</code> instead of using <code>app.mysql.query()</code> directly to get a <code>undefined</code>).</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line">exports.mysql = &#123;</span><br><span class="line">  clients: &#123;</span><br><span class="line">    <span class="comment">// clientId, access the client instance by app.mysql.get('clientId')</span></span><br><span class="line">    db1: &#123;</span><br><span class="line">      user: <span class="string">'user1'</span>,</span><br><span class="line">      password: <span class="string">'upassword1'</span>,</span><br><span class="line">      database: <span class="string">'db1'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">    db2: &#123;</span><br><span class="line">      user: <span class="string">'user2'</span>,</span><br><span class="line">      password: <span class="string">'upassword2'</span>,</span><br><span class="line">      database: <span class="string">'db2'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// default configuration for all databases</span></span><br><span class="line">  <span class="keyword">default</span>: &#123;</span><br><span class="line">    host: <span class="string">'mysql.com'</span>,</span><br><span class="line">    port: <span class="string">'3306'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li>
<p>Access the corresponding instance by <code>app.mysql.get('db1')</code></p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/post.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> posts = <span class="keyword">await</span> <span class="keyword">this</span>.app.mysql.get(<span class="string">'db1'</span>).query(sql, values);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h5 id="dynamically-instantiate"><a class="markdown-anchor" href="#dynamically-instantiate">#</a> Dynamically Instantiate</h5>
<p>Instead of declaring the configuration in the configuration file in advance, We can dynamically initialize an instance at the runtime of the application.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.beforeStart(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="comment">//  get MySQL config from configuration center &#123; host, post, password, ... &#125;</span></span><br><span class="line">    <span class="keyword">const</span> mysqlConfig = <span class="keyword">await</span> app.configCenter.fetch(<span class="string">'mysql'</span>);</span><br><span class="line">    <span class="comment">// create MySQL instance dynamically</span></span><br><span class="line">    app.database = app.mysql.createInstanceAsync(mysqlConfig);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Access the instance through <code>app.database</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/post.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> list() &#123;</span><br><span class="line">    <span class="keyword">const</span> posts = <span class="keyword">await</span> <span class="keyword">this</span>.app.database.query(sql, values);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>Attention, when creating the instance dynamically, framework would read the <code>default</code> configuration in the config file as the default configuration</strong></p>
<h3 id="plugin-locate-rule"><a class="markdown-anchor" href="#plugin-locate-rule">#</a> Plugin Locate Rule</h3>
<p>When loading the plugins in the framework, it will follow the rules to locate them as below:</p>
<ul>
<li>
<p>If there is the path configuration, load them in path directly</p>
</li>
<li>
<p>If there is no path configuration, search them with the package name, the search orders are:</p>
<ol>
<li><code>node_modules</code> directory of the application root</li>
<li><code>node_modules</code> directory of the dependencies</li>
<li><code>node_modules</code> of current directory(generally for unit test compatibility)</li>
</ol>
</li>
</ul>
<h3 id="plugin-specification"><a class="markdown-anchor" href="#plugin-specification">#</a> Plugin Specification</h3>
<p>We are very welcome your contribution to the new plugins, but also hope you follow some of following specifications:</p>
<ul>
<li>Naming Rules
<ul>
<li><code>npm</code> packages must append prefix <code>egg-</code>,and all letters must be lowercase,for example:<code>egg-xxx</code>. The long names should be concatenated with middle-lines:<code>egg-foo-bar</code>.</li>
<li>The corresponding plugin should be named in camel-case. The name should be translated according to the middle-lines of the <code>npm</code> name:<code>egg-foo-bar</code> =&gt; <code>fooBar</code>.</li>
<li>The use of middle-lines is not compulsive,for example:userservice(egg-userservice) and user-service(egg-user-service) are both acceptable.</li>
</ul>
</li>
<li><code>package.json</code> Rules
<ul>
<li>
<p>Add <code>eggPlugin</code> property according to the details discussed before.</p>
</li>
<li>
<p>For convenient index, add <code>egg</code>,<code>egg-plugin</code>,<code>eggPlugin</code> in <code>keywords</code>.</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"egg-view-nunjucks"</span>,</span><br><span class="line">  <span class="attr">"version"</span>: <span class="string">"1.0.0"</span>,</span><br><span class="line">  <span class="attr">"description"</span>: <span class="string">"view plugin for egg"</span>,</span><br><span class="line">  <span class="attr">"eggPlugin"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"nunjucks"</span>,</span><br><span class="line">    <span class="attr">"dep"</span>: [</span><br><span class="line">      <span class="string">"security"</span></span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">"keywords"</span>: [</span><br><span class="line">    <span class="string">"egg"</span>,</span><br><span class="line">    <span class="string">"egg-plugin"</span>,</span><br><span class="line">    <span class="string">"eggPlugin"</span>,</span><br><span class="line">    <span class="string">"egg-plugin-view"</span>,</span><br><span class="line">    <span class="string">"egg-view"</span>,</span><br><span class="line">    <span class="string">"nunjucks"</span></span><br><span class="line">  ],</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
</ul>
<h2 id="why-do-not-use-the-npm-package-name-as-the-plugin-name"><a class="markdown-anchor" href="#why-do-not-use-the-npm-package-name-as-the-plugin-name">#</a> Why do not use the npm package name as the plugin name?</h2>
<p>Egg defines the plugin name through the <code>eggPlugin.name</code>, it is only unique in application or framework, that means <strong>many npm packages might get the same plugin name</strong>, why design in this way?</p>
<p>First, Egg plugin do not only support npm package, it also supports search plugins in local directory. In Chapter <a href="../tutorials/progressive.html">progressive</a> we mentioned how to make progress by using these two configurations. Directory is more friendly to unit test. So, Egg can not ensure uniqueness through npm package name.</p>
<p>What's more, Egg can use this feature to make Adapter. For example, the plugin defined in<a href="./view-plugin.html#PluginNameSpecification">Template Develop Spec</a> was named as view, but there are plugins named <code>egg-view-nunjucks</code> and <code>egg-view-react</code>, the users only need to change the plugin and modify the templates, no need to modify the Controller, because all these plugins have implemented the same API.</p>
<p><strong>Giving the same plugin name and the same API to the same plugin can make quick switch between them</strong>. This is really really useful in template and database.</p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/advanced/plugin.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/advanced/plugin.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
